﻿
using Elasticsearch.Net;
using Nest;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Utility
{
    public class ElasticsearchHelper
    {

        public static Uri node;
        //public static ConnectionSettings connSettings;
        public static ElasticClient client;
        public static ElasticClient writerclient;

        public ElasticsearchHelper()
        {

            var readaddress = string.Concat("http://", getAppSettingValue("es_address"), ":", getAppSettingValue("es_readport"));
            var rnode = new Uri(readaddress);
            var readconnSettings = new ConnectionSettings(rnode);
            client = new ElasticClient(readconnSettings);


            var writeaddress = string.Concat("http://", getAppSettingValue("es_address"), ":", getAppSettingValue("es_writeport"));
            var wnode = new Uri(writeaddress);
            var writeconnSettings = new ConnectionSettings(wnode);

            writerclient = new ElasticClient(writeconnSettings);

        }


        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T">返回类型</typeparam>
        /// <param name="index">文档名称</param>
        /// <param name="queryparmlist">dic<匹配字段，匹配值></param>
        /// <param name="sortlist">dic<排序字段，是否逆序></param>
        /// <param name="size"></param>
        /// <param name="cols"></param>
        /// <returns></returns>
        public IReadOnlyCollection<T> Search<T>(string index, Dictionary<string, object> queryparmlist, Dictionary<string, bool> sortlist, int size, int? from = 0, bool scroll = false) where T : class
        {
            SearchRequest sr = new SearchRequest<T>(index);
            //sr.TrackTotalHits = true;
            try
            {
                //Stopwatch s = new Stopwatch();
                //s.Start();


                bool flag = EsSetting.EsDebug;
                if (queryparmlist != null)
                {
                    QueryContainer qc = new QueryContainer();

                    foreach (var kv in queryparmlist)
                    {
                        if (kv.Value is string)
                        {
                            var mq = new MatchQuery();

                            mq.Field = kv.Key;
                            mq.Query = kv.Value.ToString();
                            qc &= mq;
                        }
                        else if (kv.Value is int)
                        {
                            var mq = new TermQuery();

                            mq.Field = kv.Key;
                            mq.Value = kv.Value;
                            qc &= mq;
                        }
                        else if (kv.Value is List<DateTime?>)
                        {
                            var kvalue = kv.Value as List<DateTime?>;
                            if (kvalue[0] == null && kvalue[1] == null)
                                continue;

                            var mq = new DateRangeQuery();
                            mq.Field = kv.Key;

                            if (kvalue[0] != null)
                                mq.GreaterThanOrEqualTo = kvalue[0];
                            if (kvalue[1] != null)
                                mq.LessThan = kvalue[1];
                            qc &= mq;
                        }
                        else if (kv.Value is List<int?>)
                        {
                            var kvalue = kv.Value as List<int?>;
                            if (kvalue[0] == null && kvalue[1] == null)
                                continue;

                            var mq = new LongRangeQuery();
                            mq.Field = kv.Key;

                            if (kvalue[0] != null)
                                mq.GreaterThanOrEqualTo = kvalue[0];
                            if (kvalue[1] != null)
                                mq.LessThan = kvalue[1];
                            qc &= mq;
                        }
                        else if (kv.Value is string[])
                        {

                            var tsq = new TermsQuery();
                            tsq.Field = kv.Key;
                            tsq.Terms = kv.Value as string[];
                            qc &= tsq;

                        }
                    }

                    sr.Query = qc;
                }


                if (sortlist != null)
                {
                    var sorts = new List<ISort>();
                    foreach (var kv in sortlist)
                    {
                        sorts.Add(new FieldSort()
                        {
                            Field = new Field(kv.Key),
                            Order = kv.Value ? SortOrder.Descending : SortOrder.Ascending
                        });
                    }
                    sr.Sort = sorts;
                }
                sr.From = from;
                if (size != 0)
                    sr.Size = size;
                //if (index=="image_defect_table")
                //{
                //    System.IO.MemoryStream mStream = new System.IO.MemoryStream();
                //    client.SourceSerializer.Serialize(sr, mStream);
                //    var str = Encoding.UTF8.GetString(mStream.ToArray());
                //    Logger.Debug("ES查询语句："+str);
                //}


                ISearchResponse<T> response;


                if (!scroll)
                {
                    response = client.Search<T>(sr);
                    return response.Documents;
                }
                else
                {
                    sr.Scroll = "10s";
                    response = client.Search<T>(sr);
                    List<T> values = new List<T>();
                    while (response.Documents.Any())
                    {

                        values.AddRange(response.Documents);
                        response = client.Scroll<T>("10s", response.ScrollId);
                    }
                    //Logger.Debug($"滚动查询到{values.Count}个对象");
                    return values;
                }
                //标记某些场景下，输出原始请求json
                if (flag)
                {
                    System.IO.MemoryStream mStream = new System.IO.MemoryStream();
                    client.SourceSerializer.Serialize(sr, mStream);
                    var str = Encoding.UTF8.GetString(mStream.ToArray());
                    Logger.Debug(str);
                }


            }
            catch (Exception e)
            {
                System.IO.MemoryStream mStream = new System.IO.MemoryStream();
                client.SourceSerializer.Serialize(sr, mStream);
                var str = Encoding.UTF8.GetString(mStream.ToArray());
                Logger.Debug($"※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※" +
                    $"\n\rES报错语句：{index}/serach {str}\n\r" +
                    $"※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※");
                Logger.Error(e);
            }
            return null;

        }



        public Dictionary<DateTime, int> SearchDateHistogramAggs<T>(string index, Dictionary<string, object> queryparmlist, Dictionary<string, bool> sortlist, Dictionary<string, string> aggrlist) where T : class
        {
            try
            {
                SearchRequest sr = new SearchRequest<T>(index);




                if (queryparmlist != null)
                {
                    QueryContainer qc = new QueryContainer();

                    foreach (var kv in queryparmlist)
                    {
                        if (kv.Value is string || kv.Value is int)
                        {
                            var mq = new MatchQuery();
                            mq.Field = kv.Key;
                            mq.Query = kv.Value.ToString();
                            qc &= mq;
                        }
                        else if (kv.Value is List<DateTime?>)
                        {
                            var kvalue = kv.Value as List<DateTime?>;
                            if (kvalue[0] == null && kvalue[1] == null)
                                continue;

                            var mq = new DateRangeQuery();
                            mq.Field = kv.Key;

                            if (kvalue[0] != null)
                                mq.GreaterThan = kvalue[0];
                            if (kvalue[1] != null)
                                mq.LessThan = kvalue[1];
                            qc &= mq;
                        }
                        else if (kv.Value is string[])
                        {

                            var tsq = new TermsQuery();
                            tsq.Field = kv.Key;
                            tsq.Terms = kv.Value as string[];
                            qc &= tsq;

                        }
                    }

                    sr.Query = qc;
                }

                if (sortlist != null)
                {
                    var sorts = new List<ISort>();
                    foreach (var kv in sortlist)
                    {
                        sorts.Add(new FieldSort()
                        {
                            Field = new Field(kv.Key),
                            Order = kv.Value ? SortOrder.Descending : SortOrder.Ascending
                        });
                    }
                    sr.Sort = sorts;
                }

                if (aggrlist != null)
                {
                    foreach (var kv in aggrlist)
                    {
                        Logger.Debug(kv.Key);
                        Logger.Debug(kv.Value);
                        sr.Aggregations = new AggregationDictionary();
                        var dateaggr = new DateHistogramAggregation(kv.Key) { Interval = new Union<DateInterval, Time>(DateInterval.Hour), Format = "yyyy-MM-dd HH:mm:ss", Field = kv.Value };
                        sr.Aggregations.Add(kv.Key, dateaggr);
                    }
                }
                else
                {
                    return null;
                }
                sr.Size = 0;

                //System.IO.MemoryStream mStream = new System.IO.MemoryStream();
                //client.SourceSerializer.Serialize(sr, mStream);
                //var str = Encoding.UTF8.GetString(mStream.ToArray());
                //Logger.Debug(str);
                var response = client.Search<T>(sr);

                if (response.IsValid)
                {
                    Dictionary<DateTime, int> countlist = new Dictionary<DateTime, int>();
                    foreach (var item in response.Aggregations)
                    {
                        foreach (var it in (item.Value as BucketAggregate).Items)
                        {
                            var dtvalue = (it as DateHistogramBucket);
                            countlist.Add(DateTime.Parse(dtvalue.KeyAsString), (int)dtvalue.DocCount);
                        }
                        return countlist;
                    }

                }
                else
                {
                    Logger.Debug(response.DebugInformation);
                    return null;
                }
            }
            catch (Exception e)
            {
                Logger.Error(e);
            }
            return null;
        }

        public bool Write(object item)
        {
            return writerclient.IndexDocument(item).IsValid;
        }

        public bool DeleteItemByQuery(string index, string key, string value)
        {
            DeleteByQueryRequest delrequest = new DeleteByQueryRequest(index);
            var mq = new MatchQuery();
            mq.Field = key;
            mq.Query = value;
            delrequest.Query = new QueryContainer(mq);
            var str = client.RequestResponseSerializer.SerializeToString(delrequest, SerializationFormatting.Indented);
            var response = client.DeleteByQuery(delrequest);
            if (response.IsValid && response.Deleted == 1)
                return true;
            else
                return false;
        }
        public bool UpdateItemByQuery(string index, string querykey, string queryvalue, Dictionary<string, object> parms)
        {

            UpdateByQueryRequest updaterequest = new UpdateByQueryRequest(index);
            var mq = new MatchQuery();
            mq.Field = querykey;
            mq.Query = queryvalue;
            updaterequest.Query = new QueryContainer(mq);
            var scriptstr = "";
            foreach (var keyvalue in parms)
            {
                if (keyvalue.Value is string)
                {
                    scriptstr += $"ctx._source.{keyvalue.Key}='{keyvalue.Value.ToString()}';";
                }
                else if (keyvalue.Value is int || keyvalue.Value is double || keyvalue.Value is float)
                {
                    scriptstr += $"ctx._source.{keyvalue.Key}={keyvalue.Value.ToString()};";
                }
                else if (keyvalue.Value is bool)
                {
                    var boolstr = (bool)keyvalue.Value == true ? "true" : "false";
                    scriptstr += $"ctx._source.{keyvalue.Key}={boolstr};";
                }
            }
            updaterequest.Script = new InlineScript(scriptstr);
            var str = client.RequestResponseSerializer.SerializeToString(updaterequest, SerializationFormatting.Indented);
            var response = client.UpdateByQuery(updaterequest);
            if (response.IsValid && response.Updated == 1)
                return true;
            else
            {
                Logger.Info($"更新或删除{index}失败，语句：{str}");
                return false;
            }

        }
        public bool BulkUpdateItemsByQuery(string index, string querykey, string[] queryvalues, Dictionary<string, object> parms)
        {

            UpdateByQueryRequest updaterequest = new UpdateByQueryRequest(index);
            var tq = new TermsQuery();
            tq.Field = querykey;
            tq.Terms = queryvalues;
            updaterequest.Query = new QueryContainer(tq);
            var scriptstr = "";
            foreach (var keyvalue in parms)
            {
                if (keyvalue.Value is string)
                {
                    scriptstr += $"ctx._source.{keyvalue.Key}='{keyvalue.Value.ToString()}';";
                }
                else if (keyvalue.Value is int || keyvalue.Value is double || keyvalue.Value is float)
                {
                    scriptstr += $"ctx._source.{keyvalue.Key}={keyvalue.Value.ToString()};";
                }
                else if (keyvalue.Value is bool)
                {
                    var boolstr = (bool)keyvalue.Value == true ? "true" : "false";
                    scriptstr += $"ctx._source.{keyvalue.Key}={boolstr};";
                }
            }
            updaterequest.Script = new InlineScript(scriptstr);
            var str = client.RequestResponseSerializer.SerializeToString(updaterequest, SerializationFormatting.Indented);
            var response = client.UpdateByQuery(updaterequest);
            if (response.IsValid && response.Updated == queryvalues.Count())
                return true;
            else
            {
                Logger.Info($"更新或删除{index}失败，语句：{str}");
                return false;
            }

        }


        public bool AddItem<T>(string index, T obj) where T : class
        {
            IndexRequest<T> indexRequest = new IndexRequest<T>(index, null);
            indexRequest.Document = obj;
            var str = client.RequestResponseSerializer.SerializeToString(indexRequest, SerializationFormatting.Indented);
            var resoponse = client.Index(indexRequest);
            if (resoponse.IsValid)
                return true;
            else
            {
                Logger.Info($"添加{index}失败，语句：{str}");
                return false;
            }

        }




        private string getAppSettingValue(string key)
        {
            return System.Configuration.ConfigurationManager.AppSettings[key];
        }


        public async Task<IReadOnlyCollection<T>> SearchAsync<T>(string index, Dictionary<string, object> queryparmlist, Dictionary<string, bool> sortlist, int size, int? from = 0, bool scroll = false) where T : class
        {
            SearchRequest sr = new SearchRequest<T>(index);
            //sr.TrackTotalHits = true;
            try
            {
                //Stopwatch s = new Stopwatch();
                //s.Start();
                bool flag = EsSetting.EsDebug;
                if (queryparmlist != null)
                {
                    QueryContainer qc = new QueryContainer();
                    var boolquery = new BoolQuery();

                    foreach (var kv in queryparmlist)
                    {
                        if (kv.Value is string)
                        {
                            var mq = new MatchQuery();
                            mq.Field = kv.Key;
                            mq.Query = kv.Value.ToString();
                            qc &= mq;
                        }
                        else if (kv.Value is int)
                        {
                            var mq = new TermQuery();

                            mq.Field = kv.Key;
                            mq.Value = kv.Value;
                            qc &= mq;
                        }
                        else if (kv.Value is List<DateTime?>)
                        {
                            var kvalue = kv.Value as List<DateTime?>;
                            if (kvalue[0] == null && kvalue[1] == null)
                                continue;

                            var mq = new DateRangeQuery();
                            mq.Field = kv.Key;

                            if (kvalue[0] != null)
                                mq.GreaterThanOrEqualTo = kvalue[0];
                            if (kvalue[1] != null)
                                mq.LessThanOrEqualTo = kvalue[1];
                            qc &= mq;
                        }
                        else if (kv.Value is List<int?>)
                        {
                            var kvalue = kv.Value as List<int?>;
                            if (kvalue[0] == null && kvalue[1] == null)
                                continue;

                            var mq = new LongRangeQuery();
                            mq.Field = kv.Key;

                            if (kvalue[0] != null)
                                mq.GreaterThanOrEqualTo = kvalue[0];
                            if (kvalue[1] != null)
                                mq.LessThanOrEqualTo = kvalue[1];
                            qc &= mq;
                        }
                        else if (kv.Value is string[])
                        {

                            var tsq = new TermsQuery();
                            tsq.Field = kv.Key;
                            tsq.Terms = kv.Value as string[];
                            qc &= tsq;

                        }
                    }
                    boolquery.Filter = new List<QueryContainer>() { qc };
                    sr.Query = boolquery;
                }


                if (sortlist != null)
                {
                    var sorts = new List<ISort>();
                    foreach (var kv in sortlist)
                    {
                        sorts.Add(new FieldSort()
                        {
                            Field = new Field(kv.Key),
                            Order = kv.Value ? SortOrder.Descending : SortOrder.Ascending
                        });
                    }
                    sr.Sort = sorts;
                }
                sr.From = from;
                if (size != 0)
                    sr.Size = size;
                //if (index == "image")
                {
                    System.IO.MemoryStream mStream = new System.IO.MemoryStream();
                    client.SourceSerializer.Serialize(sr, mStream);
                    var str = Encoding.UTF8.GetString(mStream.ToArray());
                    //Logger.Debug("ES查询语句：" + str);
                }
                sr.Timeout = "20s";

                ISearchResponse<T> response;

                if (flag)
                {
                    System.IO.MemoryStream mStream = new System.IO.MemoryStream();
                    client.SourceSerializer.Serialize(sr, mStream);
                    var str = Encoding.UTF8.GetString(mStream.ToArray());
                    Logger.Debug("开始ES查询：" + str);
                }

                if (!scroll)
                {
                    response = await client.SearchAsync<T>(sr);

                    //Logger.Debug($"ES查询到{response.Documents.Count}个{index}对象,用时{response.Took}毫秒");

                    return response.Documents;
                }
                else
                {
                    sr.Scroll = "10s";
                    long took = 0;
                    response = await client.SearchAsync<T>(sr);
                    took += response.Took;
                    List<T> values = new List<T>();
                    while (response.Documents.Any())
                    {

                        values.AddRange(response.Documents);
                        response = await client.ScrollAsync<T>("10s", response.ScrollId);
                        took += response.Took;
                    }
                    //Logger.Debug($"ES滚动查询到{values.Count}个{index}对象,用时{took}毫秒");
                    return values;
                }
                //标记某些场景下，输出原始请求json

            }
            catch (Exception e)
            {
                System.IO.MemoryStream mStream = new System.IO.MemoryStream();
                client.SourceSerializer.Serialize(sr, mStream);
                var str = Encoding.UTF8.GetString(mStream.ToArray());
                Logger.Debug($"※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※" +
                    $"\n\rES报错语句：{index}/serach {str}\n\r" +
                    $"※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※");
                Logger.Error(e);
            }
            return null;

        }
    }

    public static class EsSetting
    {
        public static bool EsDebug=bool.Parse(ConfigHelper.GetConfig("esdebug"));
    }

}